package ccm.component.oscilloscope;

import ccm.common.Configurable;
import ccm.component.override.VFlowLayout;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.awt.GLJPanel;
import com.jogamp.opengl.util.awt.TextRenderer;

import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

import static ccm.common.Utils.timeFormatter;
import static com.jogamp.opengl.GL.*;
import static com.jogamp.opengl.GL2ES1.GL_POINT_SMOOTH;
import static com.jogamp.opengl.GL2ES1.GL_POINT_SMOOTH_HINT;

public class OscilloscopeJPanel extends JPanel implements ChangeListener, ComponentListener, ActionListener, MouseListener, MouseMotionListener, MouseWheelListener, Configurable
{
    /*
     * 颜色配置
     */
    protected static final Color[]                 colors              =new Color[]{
            Color.RED,Color.YELLOW,Color.BLUE,Color.MAGENTA,Color.ORANGE,Color.GREEN,Color.PINK,Color.LIGHT_GRAY,new Color(0XD3,0X61,0X35),new Color(112,152,95),new Color(86,81,55),new Color(0XE6,0Xaa,0X68),

            new Color(0X99,0X33,0XFF),new Color(0X00,0X99,0X99),new Color(0X99,0X99,0X33),new Color(114,234,47),new Color(245,7,115),new Color(0X00,0X66,0X33),new Color(0X99,0XCC,0X33),new Color(0XCC,0X66,0X99),
            };
    protected static final Color                   backgroundColor     =new Color(0X33,0X33,0X33);
    protected static final float                   lineWidth           =3;
    protected static final float                   axisWidth           =1;
    protected static final Color                   axisColor           =new Color(0X99,0X99,0X99);
    protected static final Font                    axisFont            =new Font("",Font.PLAIN,18);
    protected static final float                   mouseWidth          =2;
    protected static final Color                   mouseColor          =new Color(0XF2,0XF2,0XF2);
    protected static final Color                   mouseBackgroundColor=new Color(0X55,0X55,0X55,226);
    protected static final Font                    mouseFont           =new Font("",Font.PLAIN,18);
    protected static final Color                   fpsColor            =new Color(0X00,0X70,0X00);
    protected static final Font                    fpsFont             =new Font("",Font.PLAIN,20);
    protected static final long                    freshMillSecond     =15;
    /**
     * 用于绘制波形的画板
     */
    protected final        GLJPanel                glJPanel;
    /**
     * Y轴缩放输入框
     */
    protected final        JSpinner                resizeYJSpinner;
    /**
     * 自动Y轴缩放选择
     */
    protected final        JCheckBox               resizeYJCheckBox;
    /**
     * Y轴偏移输入框
     */
    protected final        JSpinner                biasYJSpinner;
    /**
     * 自动Y轴偏移选择
     */
    protected final        JCheckBox               biasYJCheckBox;
    /**
     * X轴缩放输入框
     */
    protected final        JSpinner                resizeXJSpinner;
    /**
     * X轴偏移输入框
     */
    protected final        JCheckBox               biasXJCheckBox;
    /**
     * 自动X轴偏移选择
     */
    protected final        JSpinner                biasXJSpinner;
    /**
     * 自动Y轴缩放选择
     */
    protected final        JCheckBox               keepZeroInScreenJCheckBox;
    /**
     * 全选
     */
    protected final        JCheckBox               selectAllJCheckBox;
    /**
     * 鼠标点击和拖动数据
     */
    protected final        MouseData               mouseData;
    /**
     * 数据缩放输入框
     */
    protected final        JSpinner[]              dataResizeJSpinners;
    /**
     * 数据显示选择
     */
    protected final        JCheckBox[]             dataValueJCheckBox;
    /**
     * 数据值显示
     */
    protected final        JLabel[]                dataValueJLabels;
    protected final        JPanel                  configJPanel;
    /**
     * 数据管理器
     */
    protected              OscilloscopeDataManager dataManager;
    private                boolean                 needFresh;

    public OscilloscopeJPanel(OscilloscopeDataManager dataManagerIn)
    {
        super();
        setMinimumSize(new Dimension(1,1));
        setLayout(new BorderLayout());
        dataManager=dataManagerIn;
        mouseData  =new MouseData();
        /*
         * 生成绘制波形的画板
         */

        add(glJPanel=new OscilloscopeGLJPanel(),BorderLayout.CENTER);

        /*
         * 生成左边的配置面面板
         */
        configJPanel=new JPanel();
        configJPanel.setBorder(new TitledBorder("示波器设置"));
        JScrollPane jScrollPane=new JScrollPane(configJPanel);
        jScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        add(jScrollPane,BorderLayout.EAST);
        configJPanel.setLayout(new VFlowLayout());

        /*
         * 生成的Y轴缩放面板
         */
        JPanel resizeYJSpinnerJPanel=new JPanel();
        configJPanel.add(resizeYJSpinnerJPanel);
        resizeYJSpinnerJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        resizeYJSpinnerJPanel.add(new JLabel("Y轴缩放"));

        resizeYJSpinner=new JSpinner(new SpinnerNumberModel(1,0.0001,10000,0.1));
        resizeYJSpinnerJPanel.add(resizeYJSpinner);
        resizeYJSpinner.addChangeListener(this);
        Dimension dimension=new Dimension(80,(int)(resizeYJSpinner.getFont().getSize()*1.2+10));
        resizeYJSpinner.setPreferredSize(dimension);

        resizeYJCheckBox=new JCheckBox("自动",true);
        resizeYJSpinnerJPanel.add(resizeYJCheckBox);
        resizeYJCheckBox.addActionListener(this);

        /*
         * 生成的Y轴偏移面板
         */
        JPanel biasYJSpinnerJPanel=new JPanel();
        configJPanel.add(biasYJSpinnerJPanel);
        biasYJSpinnerJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        biasYJSpinnerJPanel.add(new JLabel("Y轴偏移"));

        biasYJSpinner=new JSpinner(new SpinnerNumberModel(0,Integer.MIN_VALUE,Integer.MAX_VALUE,1));
        biasYJSpinnerJPanel.add(biasYJSpinner);
        biasYJSpinner.addChangeListener(this);
        biasYJSpinner.setPreferredSize(dimension);

        biasYJCheckBox=new JCheckBox("自动",true);
        biasYJSpinnerJPanel.add(biasYJCheckBox);
        biasYJCheckBox.addActionListener(this);

        /*
         * 生成的X轴缩放面板
         */
        JPanel resizeXJSpinnerJPanel=new JPanel();
        configJPanel.add(resizeXJSpinnerJPanel);
        resizeXJSpinnerJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        resizeXJSpinnerJPanel.add(new JLabel("X轴缩放"));

        resizeXJSpinner=new JSpinner(new SpinnerNumberModel(1,0.001,1000,0.01));
        resizeXJSpinnerJPanel.add(resizeXJSpinner);
        resizeXJSpinner.addChangeListener(this);
        resizeXJSpinner.setPreferredSize(dimension);
        resizeXJSpinner.setValue(1.0);
        updateResizeXJSpinner();

        /*
         * 生成的X轴偏移面板
         */
        JPanel biasXJSpinnerJPanel=new JPanel();
        configJPanel.add(biasXJSpinnerJPanel);
        biasXJSpinnerJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        biasXJSpinnerJPanel.add(new JLabel("X轴偏移"));

        biasXJSpinner=new JSpinner(new SpinnerNumberModel(0,0,0,50));
        biasXJSpinnerJPanel.add(biasXJSpinner);
        biasXJSpinner.addChangeListener(this);
        biasXJSpinner.setPreferredSize(dimension);

        biasXJCheckBox=new JCheckBox("刷新",true);
        biasXJSpinnerJPanel.add(biasXJCheckBox);
        biasXJCheckBox.addActionListener(this);

        /*
         * 全部开启自动
         */
        enableAutoBiasAndResize(true);

        /*
         * 生成的保持0在屏幕中的面板
         */
        JPanel keepZeroInScreenJPanel=new JPanel();
        configJPanel.add(keepZeroInScreenJPanel);
        keepZeroInScreenJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        keepZeroInScreenJCheckBox=new JCheckBox("保持0在屏幕中",true);
        keepZeroInScreenJPanel.add(keepZeroInScreenJCheckBox);
        keepZeroInScreenJCheckBox.addActionListener(this);
        /*
         * 生成的全选的面板
         */
        JPanel selectAllJPanel=new JPanel();
        configJPanel.add(selectAllJPanel);
        selectAllJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        selectAllJCheckBox=new JCheckBox("全选",true);
        selectAllJPanel.add(selectAllJCheckBox);
        selectAllJCheckBox.addActionListener(this);

        /*
         * 为各个数据生成格子的缩放面板
         */
        dataValueJLabels   =new JLabel[dataManager.getDataColumn()];
        dataResizeJSpinners=new JSpinner[dataManager.getDataColumn()];
        dataValueJCheckBox =new JCheckBox[dataManager.getDataColumn()];
        for(int i=0;i<dataManager.getDataColumn();++i)
        {
            JPanel jPanel=new JPanel();
            configJPanel.add(jPanel);
            jPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
            jPanel.add(dataValueJCheckBox[i]=new JCheckBox(dataManager.getColumnName(i),true));
            jPanel.add(dataResizeJSpinners[i]=new JSpinner(new SpinnerNumberModel(1,0.0001,1000,0.1)));
            jPanel.add(dataValueJLabels[i]=new JLabel());
            dataResizeJSpinners[i].addChangeListener(this);
            dataValueJCheckBox[i].addActionListener(this);
            // dataValueJCheckBox[i].setFont(new Font("",Font.PLAIN,20));
            // dataResizeJSpinners[i].setFont(new Font("",Font.PLAIN,20));
            // dataValueJLabels[i].setFont(new Font("",Font.PLAIN,20));
            dataValueJCheckBox[i].setForeground(colors[i%colors.length]);
            dataValueJLabels[i].setForeground(colors[i%colors.length]);
            jPanel.setPreferredSize(new Dimension(100,50));
        }
    }

    /**
     * 更新X轴缩放的范围
     */
    protected void updateResizeXJSpinner()
    {
        if(resizeXJSpinner==null||glJPanel==null)
            return;
        SpinnerNumberModel spinnerNumberModel=((SpinnerNumberModel)resizeXJSpinner.getModel());
        spinnerNumberModel.setValue(Math.max(((Number)spinnerNumberModel.getMinimum()).floatValue(),Math.min(((Number)spinnerNumberModel.getMaximum()).floatValue(),((Number)(spinnerNumberModel.getValue())).floatValue())));
    }

    /**
     * 设置自动偏移和缩放
     *
     * @param auto true表示全部开启
     *             false表示全部关闭
     */
    public void enableAutoBiasAndResize(boolean auto)
    {
        enableAutoResizeY(auto);
        enableAutoBiasX(auto);
        enableAutoBiasY(auto);
    }

    /**
     * 设置Y轴是否自动缩放
     *
     * @param auto true表示自动缩放
     *             false表示不自动缩放
     */
    protected void enableAutoResizeY(boolean auto)
    {
        resizeYJCheckBox.setSelected(auto);
        resizeYJSpinner.setEnabled(!auto);
    }

    /**
     * 设置X轴是否自动偏移
     *
     * @param auto true表示自动偏移
     *             false表示不自动偏移
     */
    protected void enableAutoBiasX(boolean auto)
    {
        biasXJCheckBox.setSelected(auto);
        biasXJSpinner.setEnabled(!auto);
    }

    /**
     * 设置Y轴是否自动偏移
     *
     * @param auto true表示自动偏移
     *             false表示不自动偏移
     */
    protected void enableAutoBiasY(boolean auto)
    {
        biasYJCheckBox.setSelected(auto);
        biasYJSpinner.setEnabled(!auto);
    }

    /**
     * 更新X轴偏移的范围
     */
    protected void updateBiasXJSpinner()
    {
        updateBiasXJSpinner(dataManager);
    }

    /**
     * 更新X轴偏移的范围
     *
     * @param dataManager 数据管理器
     */
    protected void updateBiasXJSpinner(OscilloscopeDataManager dataManager)
    {
        if(biasXJCheckBox==null||biasXJSpinner==null)
            return;
        SpinnerNumberModel spinnerNumberModel=((SpinnerNumberModel)biasXJSpinner.getModel());
        int value=(int)biasXJSpinner.getValue();
        int valueMax=dataManager.getDataLength();
        if(biasXJCheckBox.isSelected())
            value=valueMax;
        spinnerNumberModel.setValue(Math.max(0,Math.min(valueMax,value)));
        spinnerNumberModel.setMaximum(valueMax);
    }

    @Override
    public JPanel getConfigJPanel()
    {
        return configJPanel;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException
    {

    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
    {

    }

    /**
     * 窗体大小变化时更新X轴缩放并重绘
     */
    public void componentResized(ComponentEvent e)
    {
        needFresh=true;
        updateResizeXJSpinner();
        glJPanel.repaint();
    }

    public void componentMoved(ComponentEvent e)
    {

    }

    public void componentShown(ComponentEvent e)
    {

    }

    public void componentHidden(ComponentEvent e)
    {

    }

    /**
     * 输入框数据变化时重绘
     */
    public void stateChanged(ChangeEvent e)
    {
        needFresh=true;
        glJPanel.repaint();
    }

    /**
     * 选择变化时重新绘制
     */
    public void actionPerformed(ActionEvent e)
    {
        needFresh=true;
        if(e.getSource().equals(resizeYJCheckBox))
            enableAutoResizeY(resizeYJCheckBox.isSelected());
        else if(e.getSource().equals(biasYJCheckBox))
            enableAutoBiasY(biasYJCheckBox.isSelected());
        else if(e.getSource().equals(biasXJCheckBox))
            enableAutoBiasX(biasXJCheckBox.isSelected());
        else if(e.getSource().equals(selectAllJCheckBox))
            for(JCheckBox valueJCheckBox: dataValueJCheckBox)
                valueJCheckBox.setSelected(selectAllJCheckBox.isSelected());
        else
        {
            boolean checked=true;
            for(JCheckBox valueJCheckBox: dataValueJCheckBox)
                checked&=valueJCheckBox.isSelected();
            selectAllJCheckBox.setSelected(checked);
        }
        glJPanel.repaint();
    }

    public void mouseClicked(MouseEvent e)
    {
        needFresh=true;
        if(e.getButton()==MouseEvent.BUTTON1)
        {
            enableAutoResizeY(true);
            enableAutoBiasY(true);
        }
        if(e.getButton()==MouseEvent.BUTTON3)
        {
            enableAutoBiasX(true);
            if(e.getClickCount()>=2)
            {
                enableAutoResizeY(true);
                enableAutoBiasY(true);
            }
        }
    }

    /**
     * 鼠标按下时准备进入拖动状态
     */
    public void mousePressed(MouseEvent e)
    {
        needFresh=true;
        synchronized(mouseData)
        {
            mouseData.dragStart=e.getPoint();
            mouseData.DragBias =new Point((int)biasXJSpinner.getValue(),(int)biasYJSpinner.getValue());
        }
        glJPanel.repaint();
    }

    /**
     * 鼠标放开结束拖动
     */
    public void mouseReleased(MouseEvent e)
    {
        needFresh=true;
        synchronized(mouseData)
        {
            mouseData.point=e.getPoint();
        }
        glJPanel.repaint();
    }

    public void mouseEntered(MouseEvent e)
    {

    }

    /**
     * 鼠标离开时结束横纵坐标轴的显示
     */
    public void mouseExited(MouseEvent e)
    {
        needFresh=true;
        synchronized(mouseData)
        {
            mouseData.point=null;
        }
        glJPanel.repaint();
    }

    /**
     * 鼠标拖动时同步更新偏移实现拖动
     */
    public void mouseDragged(MouseEvent e)
    {
        needFresh=true;
        synchronized(mouseData)
        {
            mouseData.point=null;
            enableAutoBiasAndResize(false);
            setJSpinnerValue(biasXJSpinner,(int)(mouseData.DragBias.x-(e.getX()-mouseData.dragStart.x)/(((Number)(resizeXJSpinner.getValue())).floatValue())));
            setJSpinnerValue(biasYJSpinner,mouseData.DragBias.y+e.getY()-mouseData.dragStart.y);
        }
        glJPanel.repaint();
    }

    /**
     * 设置 JSpinner的value
     */
    protected void setJSpinnerValue(JSpinner jSpinner,Number number)
    {
        SpinnerNumberModel spinnerNumberModel=((SpinnerNumberModel)jSpinner.getModel());
        if(number.floatValue()>((Number)spinnerNumberModel.getMaximum()).floatValue())
            spinnerNumberModel.setValue(spinnerNumberModel.getMaximum());
        else if(number.floatValue()<((Number)spinnerNumberModel.getMinimum()).floatValue())
            spinnerNumberModel.setValue(spinnerNumberModel.getMinimum());
        else
            spinnerNumberModel.setValue(number);
    }

    /**
     * 鼠标移动时更新坐标轴
     */
    public void mouseMoved(MouseEvent e)
    {
        needFresh=true;
        synchronized(mouseData)
        {
            mouseData.point=e.getPoint();
        }
        glJPanel.repaint();
    }

    /**
     * 滚轮动作时更新缩放
     */
    public void mouseWheelMoved(MouseWheelEvent e)
    {
        needFresh=true;
        if(e.isControlDown())   //Y轴缩放
        {
            /*
             * Y轴缩放
             * 同步更新Y轴偏移实现顶点缩放
             */
            enableAutoResizeY(false);
            enableAutoBiasY(false);
            float k=(e.getWheelRotation()<0?1.2f:(1.0f/1.2f));
            float resizeY=((Number)(resizeYJSpinner.getValue())).floatValue();
            setJSpinnerValue(resizeYJSpinner,resizeY*k);
            k=((Number)(resizeYJSpinner.getValue())).floatValue()/resizeY;
            if(k!=1)
                setJSpinnerValue(biasYJSpinner,(int)(k*(int)biasYJSpinner.getValue()+mouseData.point.y*(1-k)));
        }
        else if(e.isAltDown())
        {
            /*
             * X轴缩放
             * 同步更新X轴偏移实现顶点缩放
             */
            enableAutoBiasX(false);
            float k=(e.getWheelRotation()<0?1.2f:(1.0f/1.2f));
            float x1=getStartI(dataManager);
            float resizeX=((Number)(resizeXJSpinner.getValue())).floatValue();
            float x2=(x1-(glJPanel.getBounds().width-e.getX())/resizeX);
            setJSpinnerValue(resizeXJSpinner,resizeX*k);
            k=resizeX/((Number)(resizeXJSpinner.getValue())).floatValue();
            if(k!=1)
                setJSpinnerValue(biasXJSpinner,(int)(x2+(x1-x2)*k));
        }
        else if(e.isShiftDown())
        {
            /*
             * X轴偏移
             */
            enableAutoBiasX(false);
            setJSpinnerValue(biasXJSpinner,(int)biasXJSpinner.getValue()-(e.getWheelRotation()*10));
        }
        else
        {
            /*
             * Y轴偏移
             */
            enableAutoBiasY(false);
            setJSpinnerValue(biasYJSpinner,(int)biasYJSpinner.getValue()-(e.getWheelRotation()*20));
        }
        glJPanel.repaint();
    }

    /**
     * 获取显示区域的起始下标
     *
     * @param dataManager 数据管理器
     * @return 显示区域的起始下标
     */
    protected int getStartI(OscilloscopeDataManager dataManager)
    {
        return Math.min(dataManager.getDataLength(),(int)biasXJSpinner.getValue())-1;
    }

    /**
     * 鼠标相关的数据
     */
    static class MouseData implements Cloneable
    {
        public Point point;
        public Point dragStart;
        public Point DragBias;

        synchronized public MouseData clone()
        {
            try
            {
                return (MouseData)super.clone();
            }catch(CloneNotSupportedException ex)
            {
                ex.printStackTrace();
            }
            return null;
        }
    }

    /**
     * 绘制波形的面板
     */
    protected class OscilloscopeGLJPanel extends GLJPanel implements GLEventListener
    {
        private final TextRenderer axisTextRenderer =new TextRenderer(axisFont);
        private final TextRenderer fpsTextRenderer  =new TextRenderer(fpsFont);
        private final TextRenderer mouseTextRenderer=new TextRenderer(mouseFont);
        /**
         * 上一次显示的时间用于计算帧率和锁帧
         */
        protected     long         lastMillSecond;
        private       int          lastDataLength;

        OscilloscopeGLJPanel()
        {
            super();
            setBackground(backgroundColor);
            addMouseListener(OscilloscopeJPanel.this);
            addMouseMotionListener(OscilloscopeJPanel.this);
            addMouseWheelListener(OscilloscopeJPanel.this);
            addComponentListener(OscilloscopeJPanel.this);
            addGLEventListener(this);
            new OscilloscopeGLJPanel.FreshThread().start();
            needFresh     =true;
            lastDataLength=-1;
            repaint();
        }

        @Override
        public void init(GLAutoDrawable glAutoDrawable)
        {
            glAutoDrawable.setAutoSwapBufferMode(false);
            axisTextRenderer.setColor(axisColor);
            fpsTextRenderer.setColor(fpsColor);
            mouseTextRenderer.setColor(mouseColor);
        }

        @Override
        public void dispose(GLAutoDrawable glAutoDrawable)
        {

        }

        @Override
        public void display(GLAutoDrawable glAutoDrawable)
        {
            /*
             * 计算帧率并进行锁帧
             */
            if(lastMillSecond+freshMillSecond>System.currentTimeMillis())
                return;
            final int fps=(int)(1000/(System.currentTimeMillis()-lastMillSecond));
            lastMillSecond=System.currentTimeMillis();
            /*
             * 复制及创建必要的变量
             */
            final OscilloscopeDataManager dataManager=OscilloscopeJPanel.this.dataManager.clone();
            if(!(OscilloscopeJPanel.this.needFresh||dataManager.getDataLength()!=lastDataLength))
                return;
            final int width=getBounds().width;
            final int height=getBounds().height;
            lastDataLength                   =dataManager.getDataLength();
            OscilloscopeJPanel.this.needFresh=false;
            MouseData nowMouseData=mouseData.clone();
            final float resizeX=((Number)(resizeXJSpinner.getValue())).floatValue();
            final int startI=getStartI(dataManager);
            final int endI=Math.max(0,(int)(startI-width/resizeX));
            final float[] dataResizeY=new float[dataManager.getDataColumn()];
            for(int j=0;j<dataManager.getDataColumn();++j)
                dataResizeY[j]=((Number)(dataResizeJSpinners[j].getValue())).floatValue();
            /*
             *计算哪些通道在屏幕上有数据
             */
            boolean[] columnInScreen=new boolean[dataManager.getDataColumn()];
            {
                for(int i=endI;i<=startI;++i)
                    for(int j=0;j<dataManager.getDataColumn();++j)
                        columnInScreen[j]|=dataManager.isColumnEnabled(i,j);
            }
            /*
             * 有数据并且需要Y轴自动缩放或者偏移就进行自动偏移的计算
             */
            /*
             * 从前往后遍历所有可以显示的数据获得最大和最小的Y值
             */
            boolean set=keepZeroInScreenJCheckBox.isSelected();
            float yMax=0;
            float yMin=0;
            for(int i=endI;i<=startI;++i)
                for(int j=0;j<dataManager.getDataColumn();++j)
                    if(dataValueJCheckBox[j].isSelected()&&dataManager.isColumnEnabled(i,j)&&columnInScreen[j])
                    {
                        float tmp=(float)(dataManager.getY(i,j)*dataResizeY[j]);
                        if(set)
                        {
                            yMax=Math.max(yMax,tmp);
                            yMin=Math.min(yMin,tmp);
                        }
                        else
                        {
                            yMax=yMin=tmp;
                            set =true;
                        }
                    }
            /*
             * 特殊处理两者相等的情况,放置出现除0异常
             */
            if(yMax==yMin)
            {
                ++yMax;
                --yMin;
            }
            /*
             * 计算偏移量并进行低通滤波
             */
            if(resizeYJCheckBox.isSelected())
            {
                float yB=(0.8f*height/(yMax-yMin))*0.5f+((Number)(resizeYJSpinner.getValue())).floatValue()*(1-0.5f);
                setJSpinnerValue(resizeYJSpinner,yB);
            }
            if(biasYJCheckBox.isSelected())
            {
                int yA=(int)(height*0.9+yMin*((Number)(resizeYJSpinner.getValue())).floatValue());
                setJSpinnerValue(biasYJSpinner,yA);
            }
            /*
             * 获取Y轴偏移量和缩放比例
             */
            final int yA=(int)biasYJSpinner.getValue();
            final float yB=((Number)(resizeYJSpinner.getValue())).floatValue();
            /*清空画板*/
            final GL2 gl=glAutoDrawable.getGL().getGL2();
            gl.glClearColor(backgroundColor.getRed()/255.0f,backgroundColor.getGreen()/255.0f,backgroundColor.getBlue()/255.0f,backgroundColor.getAlpha()/255.0f);
            gl.glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
            gl.glEnable(GL_POINT_SMOOTH);
            gl.glEnable(GL_LINE_SMOOTH);
            gl.glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);
            gl.glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
            gl.glEnable(GL_BLEND);
            gl.glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
            /*
             * 绘制坐标轴
             */
            gl.glColor4f(axisColor.getRed()/255.0f,axisColor.getGreen()/255.0f,axisColor.getBlue()/255.0f,axisColor.getAlpha()/255.0f);
            gl.glLineWidth(axisWidth);
            /*
             * 绘制竖着的坐标轴
             */
            for(int n=Integer.highestOneBit(Math.max(1,(int)(200/resizeX))), x=(int)(width-(startI*resizeX)%(n*resizeX)), i=(int)(startI-(width-x)/resizeX);x>=0&&i>=0;x-=n*resizeX,i=(int)(startI-(width-x)/resizeX))
            {
                drawLine(gl,width,height,x,0,x,height);
                drawString(axisTextRenderer,width,height,x,yA,String.format("%d",i));
            }
            /*
             * 绘制横向的坐标轴
             */
            for(int k=0;yA-k>=0||yA+k<=height;k+=100)
            {
                drawLine(gl,width,height,0,yA-k,width,yA-k);
                drawLine(gl,width,height,0,yA+k,width,yA+k);
                // bufG.drawString(String.format("%.2f",(yA-(yA-k))*yB),0,yA-k);
                // bufG.drawString(String.format("%.2f",(yA-(yA+k))*yB),0,yA+k);
            }
            /*
             * 绘制曲线
             */
            if(dataManager.getDataLength()>0&&startI>=0)
            {
                final int[] lastI=new int[dataManager.getDataColumn()];
                final float[] lastY=new float[dataManager.getDataColumn()];
                for(int j=0;j<dataManager.getDataColumn();++j)
                    if(dataValueJCheckBox[j].isSelected()&&columnInScreen[j]&&dataManager.isColumnEnabled(endI,j))
                        lastY[j]=dataManager.getY(endI,j);
                for(int i=endI+1;i<=startI;++i)
                    for(int j=0;j<dataManager.getDataColumn();++j)
                        if(dataValueJCheckBox[j].isSelected()&&columnInScreen[j]&&dataManager.isColumnEnabled(i,j))
                        {
                            gl.glColor4f(colors[j%colors.length].getRed()/255.0f,colors[j%colors.length].getGreen()/255.0f,colors[j%colors.length].getBlue()/255.0f,colors[j%colors.length].getAlpha()/255.0f);
                            gl.glLineWidth(lineWidth);
                            final float y=(float)dataManager.getY(i,j);
                            final float x1=((width-(startI-i)*resizeX));
                            final float x2=((width-(startI-lastI[j])*resizeX));
                            if(x1-x2>=1)
                            {
                                final float y1=yA-yB*y*dataResizeY[j];
                                final float y2=yA-yB*lastY[j]*dataResizeY[j];
                                drawLine(gl,width,height,x1,y1,x2,y2);
                                lastY[j]=y;
                                lastI[j]=i;
                            }
                        }
            }


            /*
             * 绘制鼠标轴
             */
            gl.glLineWidth(mouseWidth);
            gl.glColor4f(mouseColor.getRed()/255.0f,mouseColor.getGreen()/255.0f,mouseColor.getBlue()/255.0f,mouseColor.getAlpha()/255.0f);
            if(nowMouseData.point!=null)
            {
                drawLine(gl,width,height,nowMouseData.point.x,0,nowMouseData.point.x,height);
                drawLine(gl,width,height,0,nowMouseData.point.y,width,nowMouseData.point.y);
            }

            /*
             * 绘制鼠标上的数据点
             */
            if(nowMouseData.point!=null&&(dataManager.getDataLength()>0&&startI>=0))
            {
                final int mouthI=(int)((startI-(width-nowMouseData.point.x)/resizeX));
                if(mouthI>=0)
                {

                    final int fontSize=mouseFont.getSize();
                    //当前所在的数据行
                    //当前所在的数据行对应数据列的值
                    final float[] y=new float[dataManager.getDataColumn()];
                    //显示值的坐标
                    final int[] y1=new int[dataManager.getDataColumn()];
                    mouseTextRenderer.setColor(mouseColor);
                    {
                        final String text=String.format("%d",mouthI);
                        drawString(mouseTextRenderer,width,height,nowMouseData.point.x-fontSize*text.length()/2-fontSize,nowMouseData.point.y-fontSize-4,text);
                    }
                    {
                        final String text=timeFormatter(dataManager.getTimeUs(mouthI));
                        drawString(mouseTextRenderer,width,height,nowMouseData.point.x-fontSize*text.length()/2-fontSize,nowMouseData.point.y-4,text);
                    }
                    {
                        /*
                         * 两次遍历确保所有的数都能画上
                         */
                        final int[] order=new int[dataManager.getDataColumn()];
                        final boolean[] used=new boolean[dataManager.getDataColumn()];
                        for(int j=0;j<dataManager.getDataColumn();++j)
                            if(dataManager.isColumnEnabled(mouthI,j)&&columnInScreen[j])
                                y[j]=dataManager.getY(mouthI,j);
                        int minY1=height;
                        for(int j=0;j<dataManager.getDataColumn();++j)
                        {
                            int pos=-1;
                            for(int k=0;k<dataManager.getDataColumn();++k)
                                if(!used[k]&&(pos==-1||y[k]<=y[pos]))
                                    pos=k;
                            used[pos]=true;
                            order[j] =pos;
                            if(dataValueJCheckBox[pos].isSelected()&&columnInScreen[pos])
                            {
                                y1[pos]=(int)Math.min(minY1,Math.max(2*fontSize+8,Math.min((int)(yA-yB*y[pos]*dataResizeY[pos]),height)));
                                minY1  =(int)(y1[pos]-2*fontSize-8);
                            }
                        }
                        minY1=(int)(2*fontSize+8);
                        for(int j=dataManager.getDataColumn()-1;j>=0&&y1[order[j]]<=minY1&&dataValueJCheckBox[order[j]].isSelected()&&columnInScreen[order[j]];--j)
                        {
                            y1[order[j]]=minY1;

                            minY1+=(int)(2*fontSize+8);
                        }
                    }
                    /*
                     * 把数值画到画板上
                     */
                    for(int j=0;j<dataManager.getDataColumn();++j)
                        if(dataValueJCheckBox[j].isSelected()&&columnInScreen[j])
                        {
                            final String text=String.format("%-10.6f",y[j]);
                            {
                                int xx1=nowMouseData.point.x;
                                int yy1=y1[j]-(int)(fontSize*2+8);
                                int xx2=xx1+fontSize*text.length()/2+12;
                                int yy2=yy1+(int)(fontSize*2+8);
                                gl.glColor4f(mouseBackgroundColor.getRed()/255.0f,mouseBackgroundColor.getGreen()/255.0f,mouseBackgroundColor.getBlue()/255.0f,mouseBackgroundColor.getAlpha()/255.0f);
                                gl.glBegin(GL2.GL_QUADS);
                                drawPoint(gl,width,height,xx1,yy1);
                                drawPoint(gl,width,height,xx1,yy2);
                                drawPoint(gl,width,height,xx2,yy2);
                                drawPoint(gl,width,height,xx2,yy1);
                                gl.glEnd();
                            }
                            mouseTextRenderer.setColor(colors[j%colors.length]);
                            drawString(mouseTextRenderer,width,height,nowMouseData.point.x+4,y1[j]-4,text);
                            dataValueJLabels[j].setText(text);
                            drawString(mouseTextRenderer,width,height,nowMouseData.point.x+4,(int)(y1[j]-fontSize*1.2-4),dataManager.getColumnName(j));
                        }
                }
                else
                {
                    for(int i=0;i<dataManager.getDataColumn();++i)
                        dataValueJLabels[i].setText("");
                }
            }
            else
            {
                for(int i=0;i<dataManager.getDataColumn();++i)
                    dataValueJLabels[i].setText("");
            }


            /*
             * 显示帧率
             */
            gl.glColor4f(fpsColor.getRed()/255.0f,fpsColor.getGreen()/255.0f,fpsColor.getBlue()/255.0f,fpsColor.getAlpha()/255.0f);
            {
                String text=String.format("%d,%d",startI-endI+1,dataManager.getDataLength());
                drawString(fpsTextRenderer,width,height,0,fpsFont.getSize()*2,text);
            }
            {
                String text=String.format("%d fps,%d ms",fps,System.currentTimeMillis()-lastMillSecond);
                drawString(fpsTextRenderer,width,height,0,fpsFont.getSize(),text);
            }

            gl.glFlush();
            glAutoDrawable.swapBuffers();
        }

        @Override
        public void reshape(GLAutoDrawable glAutoDrawable,int i,int i1,int i2,int i3)
        {

        }

        private void drawLine(final GL2 gl,int width,int height,float x1,float y1,float x2,float y2)
        {
            gl.glBegin(GL2.GL_LINES);
            drawPoint(gl,width,height,x1,y1);
            drawPoint(gl,width,height,x2,y2);
            gl.glEnd();
        }

        private void drawString(final TextRenderer textRenderer,int width,int height,int x,int y,final String string)
        {
            textRenderer.beginRendering(width,height);
            textRenderer.draw(string,x,height-y);
            textRenderer.endRendering();
        }

        private void drawPoint(final GL2 gl,int width,int height,float x,float y)
        {
            gl.glVertex2f(-1.0f+2.0f*x/width,1.0f-2.0f*y/height);
        }

        /**
         * 刷新面板用的线程类
         */
        protected class FreshThread extends Thread
        {
            public void run()
            {
                while(true)
                {
                    try
                    {
                        updateBiasXJSpinner();
                        glJPanel.repaint();
                        Thread.sleep(freshMillSecond);
                    }catch(InterruptedException e)
                    {
                        e.printStackTrace();
                        break;
                    }catch(Exception e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        }

    }
}
